/*******************************************************************/
/*    Copyright (c) 1999-2020 by Spatial Corp.                     */
/*    All rights reserved.                                         */
/*    Protected by U.S. Patents 5,257,205; 5,351,196; 6,369,815;   */
/*                              5,982,378; 6,462,738; 6,941,251    */
/*    Protected by European Patents 0503642; 69220263.3            */
/*    Protected by Hong Kong Patent 1008101A                       */
/*******************************************************************/

//----------------------------- IopAcisVisualization.cpp -------------------------------

//Acis
#include "lists.hxx"
#include <list>
//IOP
#include "SPAIop.h"
#include "SPAIopAcis.h"
#include "SPAIopVisu.h"
#include "SPAIopSampleUtilities.h"
#include "SPAIopPrintUtilities.h"
#include "SPAIopAcisSampleUtilities.h"
#include "SPAIopVisualization.h"
#include "SPAIopArgParser.h"
#include "SPAIopSampleResultUtils.h"
#include "SPAIopPerformanceStatistics.h"

#define CurrentReferenceType SPAIopOutputPSReference
#include "SPAIopVisualizationTransfer.h"
#undef CurrentReferenceType 

// License file
#include "spatial_license.h"
#include "spa_unlock_result.hxx"

//MP
#include "SPAIopMultiProcessBatchDefinition.h"
#include "SPAIopMultiProcessSpoolerEx.h"

#include<unordered_map>
#include<unordered_set>

//-----------------------------------------------------------------------------------------

using namespace SPAIopSampleLogging;
using namespace SPAIopPrintUtilities;
using namespace SPAIopAcisPrintUtils;
using namespace SPAIopVisualizationTransfer;
using namespace SPAIopAcisPSVisualizationUtils;

//-----------------------------------------------------------------------------------------

static bool _preserveStructure = true;
static bool _useInputVisualization = false;
static bool _runInMP = false;

//-----------------------------------------------------------------------------------------
class ProgressReporter;
ProgressReporter* _g_pProgressReporter = nullptr;
//-----------------------------------------------------------------------------------------

bool ExportSyntheticData(const wchar_t* ipDestinationFilePath, const SPAIopPolicy& iPolicy);

int ImportExportVisualizationData(const wchar_t* ipSourceFilePath, const wchar_t* ipDestinationFilePath, const SPAIopPolicy& iPolicy);
int DefineOutputProductStructure(SPAIopInputProductStructure& iInputPS, SPAIopPSImportResult& iInputPSResult, const SPAIopPolicy& iPolicy, SPAIopOutputProductStructure& oOutputPS, int& opCopiedInstances);
int DefinePSReference(SPAIopInputPSReference& iInputReference, SPAIopInputPSMissingFileIter& iMissingFiles, SPAIopOutputProductStructure& iOutputPS, const SPAIopPolicy& iPolicy, SPAIopOutputPSReference& oOutputReference, int& opNumDefined, std::unordered_map<int, std::pair<bool, double[3]>>& iRefIDToShiftTranslationMap, bool& oIsShiftTranslationVectorApplied, double(&oShiftTranslation)[3]);
int DefinePSReferenceMP(SPAIopInputPSReference& iInputReference, SPAIopInputPSMissingFileIter& iMissingFiles, SPAIopOutputProductStructure& iOutputPS, const SPAIopPolicy& iPolicy, SPAIopOutputPSReference& oOutputReference, int& opNumDefined, std::unordered_map<int, SPAIopOutputPSReference*>& iIdToOutputPSRef, std::unordered_map<int, std::pair<std::list<SPAIopInputPSInstance>, std::list<SPAIopOutputPSInstance*>>>& iRefIDToListOfAllInOutInst, SPAIopRep& iTokenRepType, bool& oIsShiftTranslationVectorApplied, double(&oShiftTranslation)[3]);
void CreateOutputProductStructureSkeletonRootInstance(SPAIopInputProductStructure& iInputPS, SPAIopPSImportResult& iInputPSResult, SPAIopOutputProductStructure& oOutputPS, std::unordered_map<int, SPAIopOutputPSReference*>& iIdToOutputPSRef, std::unordered_map<int, std::pair<std::list<SPAIopInputPSInstance>, std::list<SPAIopOutputPSInstance*>>>& iRefIDToListOfAllInOutInst);
void CreateOutputProductStructureSkeleton(SPAIopInputPSReference& iInputReference, SPAIopInputPSMissingFileIter& iMissingFiles, SPAIopOutputProductStructure& iOutputPS, SPAIopOutputPSReference& oOutputReference, std::unordered_map<int, SPAIopOutputPSReference*>& iIdToOutputPSRef, std::unordered_map<int, std::pair<std::list<SPAIopInputPSInstance>, std::list<SPAIopOutputPSInstance*>>>& iRefIDToListOfAllInOutInst);

int ImportVisualizationData(const wchar_t* ipSourceFilePath);
int ImportParts(ImportedPartReferenceList iImportedPartList, const SPAIopPolicy& iPolicy);
int ImportPartVisualization(SPAIopPartDocument& iIopPartDocument, const SPAIopPolicy& iPolicy);
void ImportAssemblyLevelVisualization(SPAIopInputProductStructure& iInputPS, const SPAIopPolicy& iPolicy);
size_t GetNonMissingParts(SPAIopInputProductStructure& iInputPSStructure, SPAIopPSImportResult& iInputPSResult);

//-----------------------------------------------------------------------------------------
void PrintHelp()
{
	PrintMessage(VERBOSITY_LEVEL_NONE, L"Usage: IopAcisVisualization -i <input-path> [-o <output-path>] [-dump <dump-path>] [-p <policy-path>] [-log <full-log-file-path>] [-s <0/1>] [-v <verbosity-level>] [-useInputVisu <0/1>] [-mode MP] [-visuCurveData <0/1>] [-h] \n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-i <path>           : The absolute path to the input file. \n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-o <path>           : The absolute path to the output file. This can be any format for which 3D InterOp supports writing visualization, eg: .stl, .3dxml, .xcgm etc. \n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-dump <path>        : The absolute path to a file preserving console output.\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-p <path>           : The absolute path to the policy file used to control optional behavior during operations.\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-log <path>         : The absolute path to the log file.\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-s <0/1>            : Whether to preserve the source data structure (1, default) or reorganize to collect graphics (0).\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-v <0..4>           : The verbosity level of the console output; valid values range from 0 (none) to 4 (full, default). This is only meaningful when -i is specified but not -o.\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-useInputVisu <0/1> : Whether to directly use the visualization data stored in the input file. This is only meaningful when both -i and -o are specified.\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t                      0 = Create new visualization data for the output by traversing the input visualization data (default).\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t                      1 = Set the input visualization data on the output.\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-mode <mode>        : MP to run in multi process mode; otherwise run in a single process (default).\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-visuCurveData <0/1>        : 1 to get original curve data, otherwise 0 for converted polyline data (default).\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\t-h                  : This help.\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"\nIf -i is specified but not -o, then a dump of the visualization is printed with -v verbosity.\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"If -i and -o are specified, then visualization read from -i is written to -o through the authoring interface.\n");
	PrintMessage(VERBOSITY_LEVEL_NONE, L"If -o is specified but not -i, then an output file is generated from hard-coded data for demonstration.\n");
	return;
}
//-----------------------------------------------------------------------------------------
class ProgressReporter
{
public:

	//-----------------------------------------------------------------------------------------

	ProgressReporter() : _psDone(false), _numParts(0), nonMissingParts(0), _partsDone(0), _lastProgress(-1),
						 _partsVizDone(false), _exportStarts(false) {}

	//-----------------------------------------------------------------------------------------

	void ProgressCallBack(double iProgress, bool& iAbort)
	{
		int progressRange = 100;
		int percent = 0;

		double actualProgressPercent = iProgress * 100;
		int progress = static_cast<int>(actualProgressPercent);

		if (!_psDone || _exportStarts)
		{
			percent = (int)((100.0*progress) / double(progressRange));
			if (_exportStarts)
				PrintMessage(L"Export, Output Product Structure with Parts, Progress : %d%%\n", percent);
			else
				PrintMessage(L"Import, Input Product Structure, Progress : %d%%\n", percent);
		}
		else if (!_partsVizDone)
		{
			size_t cnt = _partsDone * progressRange + progress;
			percent = (int)((100.0*cnt) / (nonMissingParts*progressRange));
			if (percent > _lastProgress)
				PrintMessage(L"Import, Parts of Input Product Structure, Progress : %d%%\n", percent);
			_lastProgress = percent;
			if (progress == progressRange)
				_partsDone += 1;
		}
		else
		{
			percent = (int)((100.0*progress) / double(progressRange));
			PrintMessage(L"Importing Assembly Visualization Progress : %d%%\n", percent);
		}
	}

	//-----------------------------------------------------------------------------------------

	void SetNumParts(int iNumParts)
	{
		_numParts = iNumParts;
	}

	//-----------------------------------------------------------------------------------------

	void setNonMissingParts(size_t iNonMissingParts)
	{
		nonMissingParts = iNonMissingParts;
	}

	//-----------------------------------------------------------------------------------------

	void SetPsDone(bool iIsDone)
	{
		_psDone = iIsDone;
	}

	//-----------------------------------------------------------------------------------------

	void SetExportStarts(bool isStart)
	{
		_exportStarts = isStart;
		if (_exportStarts)
			PrintMessage(L"\n Exporting Product Structure\n");
	}

	//-----------------------------------------------------------------------------------------
	
	void SetImportingPartVizDone(bool iIsDone)
	{
		_partsVizDone = iIsDone;
	}
	//-----------------------------------------------------------------------------------------

private:

	//-----------------------------------------------------------------------------------------

	bool _psDone;
	bool _partsVizDone;
	
	size_t _numParts;
	size_t nonMissingParts;
	size_t _partsDone;
	int _lastProgress;
	bool _exportStarts;

	//-----------------------------------------------------------------------------------------

};

//-----------------------------------------------------------------------------------------

void MyProgressCallBack(double iProgress, bool& iAbort)
{
	if (nullptr != _g_pProgressReporter)
	{
		_g_pProgressReporter->ProgressCallBack(iProgress, iAbort);
	}
}
//-----------------------------------------------------------------------------------------

bool ExportSyntheticData( const wchar_t* ipDestinationFilePath, const SPAIopPolicy& iPolicy )
{
	// the simplest output product structure has one instance of one reference
    bool returnValue = true;
	SPAIopWString destinationPath( ipDestinationFilePath );

	SPAIopOutputProductStructure outputPS;
	if ( FabricateSimpleOutputStructure( outputPS ))
	{
		SPAIopProductStructureExporter exporter;
		try 
		{
			SPAIopResult res = exporter.ExportWithPolicy( iPolicy, outputPS, destinationPath );
		}
		catch( SPAIopAcisError& fatalErr )
		{
			DisplayError(fatalErr);
			returnValue = false;
		}
		catch ( ... )
		{
			std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
		}
	}
	return returnValue;
}

int DefinePSReferenceMP(SPAIopInputPSReference& iInputReference, SPAIopInputPSMissingFileIter& iMissingFiles, SPAIopOutputProductStructure& iOutputPS, const SPAIopPolicy& iPolicy, SPAIopOutputPSReference& oOutputReference, int& opNumDefined, std::unordered_map<int, SPAIopOutputPSReference*>& iIdToOutputPSRef, std::unordered_map<int, std::pair<std::list<SPAIopInputPSInstance>, std::list<SPAIopOutputPSInstance*>>>& iRefIDToListOfAllInOutInst, SPAIopRep& iTokenRepType, bool& oIsShiftTranslationVectorApplied, double(&oShiftTranslation)[3])
{
	int numDefined = 0;
	int result = 0;
	if (!oOutputReference.IsDefined())
	{
		if (iInputReference.IsPart())    // part node
		{
			// Import the visualization data of a part using the Import method of a 
			// SPAIopAcisVisualizationImporter object.  Use try/catch exception handling
			// blocks to manage fatal errors.

			SPAIopVisualizationImporter partVizImporter;
			SPAIopPartDocument refPartDocument = iInputReference.GetDocument();
			unsigned int uniqueID = iInputReference.GetUniqueID();
			SPAIopWString storageNamePath = iInputReference.GetStorageName();
			SPAIopWString referenceName = iInputReference.GetReferenceName();

			SPAIopWString storageName;
			GetNameWithExtension(storageNamePath, storageName);

			const wchar_t* pStorageName = nullptr;
			storageName.ToWChar(pStorageName);

			const wchar_t* pReferenceName = nullptr;
			referenceName.ToWChar(pReferenceName);
			try
			{
				SPAIopVisuImportResult vizImportResult = partVizImporter.ImportWithPolicy(iPolicy, refPartDocument);
				PrintMessage(L"\n Processing reference part. UniqueID = %d, Storage Name = %ls, Reference Name = %ls \n", uniqueID, pStorageName, pReferenceName);
				SPAIopVisuData vizInData = vizImportResult.GetVisualizationData();

				oIsShiftTranslationVectorApplied = vizImportResult.GetShiftTranslation(oShiftTranslation[0], oShiftTranslation[1], oShiftTranslation[2]);
				if (oIsShiftTranslationVectorApplied)
				{
					PrintMessage(L"\nGeometry is shifted near the origin to fit it inside the current modeler range, Following Shift Translation Vector should be applied on the part to get the original input coordinates of geometry:- X: %lf\tY: %lf\tZ: %lf\n", oShiftTranslation[0], oShiftTranslation[1], oShiftTranslation[2]);
				}

				const SPAIopLengthUnitType visualizationUnit = vizInData.GetUnits();
				if (visualizationUnit != SPAIopLengthUnit_Unknown)
				{
					SPAIopWString unitStr = SPAIopLengthUnitUtils::LengthUnitToString(visualizationUnit);
					const wchar_t* pUnitArray = NULL;
					unitStr.ToWChar(pUnitArray);
					PrintMessage(L"\nUnit of Visualization Data:- %ls\n", pUnitArray);

					//Scaling Shift translation vector to modeler unit as we will accomodate it as instance transform, instance transform is given in modeler unit
					if (oIsShiftTranslationVectorApplied)
					{
						double unitScale = 1.0;
						SPAIopLengthUnitType modelerUnit = SPAIopLengthUnit_Unknown;
						SPAIopModelerOptionsAcis acisModelerOptions;
						const bool res = iPolicy.GetCurrentModelerOptions(acisModelerOptions);
						if (res)
						{
							const bool foundOption = acisModelerOptions.GetCurrentModelerUnits(modelerUnit);
							if (!foundOption)
								modelerUnit = SPAIopLengthUnit_Millimeter;
						}
						else
							modelerUnit = SPAIopLengthUnit_Millimeter;

						unitScale = SPAIopLengthUnitUtils::GetScalingFactor(visualizationUnit, modelerUnit);
						oShiftTranslation[0] = oShiftTranslation[0] * unitScale;
						oShiftTranslation[1] = oShiftTranslation[1] * unitScale;
						oShiftTranslation[2] = oShiftTranslation[2] * unitScale;
					}
				}

				if (!_useInputVisualization)
				{
					SPAIopVisuOutputData vizOutData;
					numDefined += TransferVisualizationData(vizInData, vizOutData);
					oOutputReference.SetOutputVisualization(vizOutData);
				}
				else
				{
					oOutputReference.SetVisualization(vizInData);
					if (0 == numDefined)
					{
						numDefined += 1;
					}
				}
			}
			catch (SPAIopAcisError& error)
			{
				result = SPAIopSampleResult_Import_Part_Fail;
				PrintMessage(L"\n FAILURE : Part failed to import. UniqueID = %d, Storage Name = %ls, Reference Name = %ls \n", uniqueID, pStorageName, pReferenceName);
				DisplayError(error);
			}
			catch (...)
			{
				PrintMessage(L"\n FAILURE : Part failed to import. UniqueID = %d, Storage Name = %ls, Reference Name = %ls \n", uniqueID, pStorageName, pReferenceName);
				result = SPAIopSampleResult_Import_Part_Fail;
			}
		}
		else   // sub-assembly node
		{
			// build a map of references to use later for attaching assembly annotations
			const wchar_t* refName = nullptr;
			SPAIopWString referencePID;
			bool hasPID = iInputReference.GetPID(referencePID);
			if (hasPID)
				referencePID.ToWChar(refName);
			SPAIopOutputPSReference* pReference = &oOutputReference;
			TrackReferences(add_subassembly, refName, pReference);

			int nbInstances = iInputReference.GetInstancesCount();
			if (nbInstances > 0)
			{
				for (int i = 0; i < nbInstances; i++)
				{
					// Get Input Product Structure info (instance, reference, ID)
					SPAIopInputPSInstance iNodeInstance = iInputReference.GetInstance(i);
					SPAIopInputPSReference iNodeReference = iNodeInstance.GetReference();
					SPAIopWString referenceStorageName = iNodeReference.GetStorageName();
					bool isMissingReference = iMissingFiles.ContainsPath(referenceStorageName);
					if (false == isMissingReference)
					{
						unsigned int refID = iNodeReference.GetUniqueID();

						// Replicate the same for the output product structure
						SPAIopOutputPSReference newOutputReference = *(iIdToOutputPSRef[refID]);
						int entities = 0;
						bool isShiftTranslationVectorApplied = false;
						double shiftTranslationVector[3] = { 0.0 };
						int res = DefinePSReferenceMP(iNodeReference, iMissingFiles, iOutputPS, iPolicy, newOutputReference, entities, iIdToOutputPSRef, iRefIDToListOfAllInOutInst, iTokenRepType, isShiftTranslationVectorApplied, shiftTranslationVector);// Recursively navigate nodes below this sub-assembly node
						if (0 != res)
							result = res;
						if (entities > 0)
						{
							numDefined += entities;

							if (isShiftTranslationVectorApplied)
							{
								const auto unorderedMapIterator = iRefIDToListOfAllInOutInst.find(refID);
								if (unorderedMapIterator != iRefIDToListOfAllInOutInst.end())
								{
									//Applying shift translation to all the instances having current reference/RefID
									//we don't process previously defined refs again
									while (!(unorderedMapIterator->second.first.empty()))
									{
										SPAIopInputPSInstance inputInstance = unorderedMapIterator->second.first.front();
										SPAIopOutputPSInstance outputPSInstance = *(unorderedMapIterator->second.second.front());
										ModifyInstanceTransform(inputInstance, shiftTranslationVector, outputPSInstance);
										unorderedMapIterator->second.first.pop_front();
										delete (unorderedMapIterator->second.second.front());
										unorderedMapIterator->second.second.pop_front();
									}
								}
							}
						}
					}
				}
			}
		}

		// In some formats supporting delayed part loading, we do not get reference name without loading (importing) part first. So set it after part is imported.
		SPAIopWString referenceName = iInputReference.GetReferenceName();
		oOutputReference.SetName(referenceName);

		// mark this reference as fully defined so we don't go through this code again if it is used by another instance
		if (numDefined > 0)
		{
			oOutputReference.SetDefined(true);
		}
	}
	else
	{
		// Don't do anything for previously defined refs, but pretend they were defined this time
		numDefined = 1;
	}

	opNumDefined = numDefined;
	return result;
}

//-----------------------------------------------------------------------------------------
bool ImportProductStructure(const wchar_t*& ipSourceFilePath, const SPAIopPolicy& iPolicy, SPAIopInputProductStructure& oInputPS, SPAIopPSImportResult &oInputPSResult )
{
	bool returnValue = false;
	// Import the Assembly tree as a Product Structure.  Note that 
	// importing a Product Structure does not import any visualization associated
	// with the model, which is done separately from part-associated reference nodes
	// in the Product Structure.  Use try/catch exception handling blocks to 
	// manage fatal errors.

	// Create Source Document from input file
	std::wstring sourceFilePath( ipSourceFilePath );
	SPAIopWString wSourceFilePath( sourceFilePath.c_str());
	SPAIopDocument sourceDoc( wSourceFilePath );

	// Create a Product Strcuture Importer
	SPAIopProductStructureImporter psImporter;

	try
	{
		if(nullptr != _g_pProgressReporter)
			psImporter.SetProgressCallBack((SPAIopProgressCallback)MyProgressCallBack);

		oInputPSResult = psImporter.ImportWithPolicy( iPolicy, sourceDoc, oInputPS );
	
		// Dump missing files in the Product Structure
		DumpMissingFilesInProductStructure( oInputPSResult );
		returnValue = true;
		
		if (nullptr != _g_pProgressReporter)
		{
			_g_pProgressReporter->SetNumParts(oInputPS.GetUniqueReferencesCount());
			size_t numNonMissingParts = GetNonMissingParts(oInputPS, oInputPSResult);
			_g_pProgressReporter->setNonMissingParts(numNonMissingParts);
			_g_pProgressReporter->SetPsDone(true);
		}
	}
	catch( SPAIopAcisError& error )
	{
		DisplayError( error );
	}
	catch ( ... )
	{
		std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
	}

	return returnValue;
}
//-----------------------------------------------------------------------------------------

int ImportExportVisualizationData( const wchar_t* ipSourceFilePath, const wchar_t* ipDestinationFilePath, 
								   const SPAIopPolicy& iPolicy)
{
    int returnValue = SPAIopSampleResult_Import_Ok;

	_g_pProgressReporter = new ProgressReporter();
	// Import Input Product Structure 
    const wchar_t* ipPolicyName = nullptr;
	SPAIopInputProductStructure inputPS;
	SPAIopPSImportResult inputPSResult;
	bool gotPS = ImportProductStructure( ipSourceFilePath, iPolicy, inputPS, inputPSResult );

	if ( !gotPS ) 
	{
		// If the product structure can't be loaded there's no way to translate anything
		return SPAIopSampleResult_Import_PS_Fail;
	}

	SPAIopOutputProductStructure outputPS;
	int numCopiedInstances = 0;

	PrintMessage(L"\n Importing Part level Visualization for all Parts. \n");
		
	returnValue = DefineOutputProductStructure(inputPS, inputPSResult, iPolicy, outputPS,numCopiedInstances);

	_g_pProgressReporter->SetImportingPartVizDone(true);

	try
	{
		numCopiedInstances += TransferAssemblyVisualization(inputPS, outputPS);
	}
	catch (SPAIopAcisError &error) 
	{
		DisplayError(error);
	}
	catch (...)
	{
		std::wcout << "FAILURE: Unknown exception caught from TransferAssemblyVisualization!" << std::endl;
	}
	
	if (nullptr != _g_pProgressReporter)
		_g_pProgressReporter->SetExportStarts(true);

	if ( numCopiedInstances > 0 ) 
	{
		SPAIopWString wTargetFilePath( ipDestinationFilePath );
		SPAIopProductStructureExporter exporter;
		try 
		{
			exporter.SetProgressCallBack((SPAIopProgressCallback)MyProgressCallBack);
			SPAIopResult res = exporter.ExportWithPolicy( iPolicy, outputPS, wTargetFilePath );
		}
		catch( SPAIopAcisError& error )
		{
			DisplayError( error );
			returnValue = SPAIopSampleResult_Export_PS_Fail;
		}
		catch( ... )
		{
			std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
			returnValue = SPAIopSampleResult_Export_PS_Fail;
		}
	} 

	delete _g_pProgressReporter;
	_g_pProgressReporter = nullptr;

	return returnValue;
}

//-----------------------------------------------------------------------------------------

int ImportVisualizationData( const wchar_t* ipSourceFilePath, const SPAIopPolicy& iPolicy)
{
    int returnValue = SPAIopSampleResult_Import_Ok;

	_g_pProgressReporter = new ProgressReporter();

	// Import Input Product Structure 
	SPAIopInputProductStructure inputPS;
	SPAIopPSImportResult inputPSResult;
	bool gotPS = ImportProductStructure( ipSourceFilePath, iPolicy, inputPS, inputPSResult );

	if (true == gotPS)
	{		
		ImportedPartReferenceList importedPartList;
		ImportPartReference(inputPS, inputPSResult, importedPartList);

		PrintMessage(L"\n Importing Part level Visualization from all Parts. \n");
		returnValue = ImportParts(importedPartList, iPolicy);
			
		if (nullptr != _g_pProgressReporter)
			_g_pProgressReporter->SetImportingPartVizDone(true);

		importedPartList.clear();

		// Import Assembly level Visualization Data
		ImportAssemblyLevelVisualization(inputPS, iPolicy);
	}
	else
		returnValue = SPAIopSampleResult_Import_PS_Fail;

	if (nullptr != _g_pProgressReporter)
	{
		delete _g_pProgressReporter;
		_g_pProgressReporter = nullptr;
	}
	return returnValue;
}

//-----------------------------------------------------------------------------------------
size_t GetNonMissingParts(SPAIopInputProductStructure& iInputPSStructure, SPAIopPSImportResult &iInputPSResult)
{
	size_t missingParts = 0;
	size_t totalParts = iInputPSStructure.GetUniqueReferencesCount();
	SPAIopInputPSMissingFileIter missingFiles = iInputPSResult.GetMissingFileIterator();
	int nInstances = iInputPSStructure.GetUniqueReferencesCount();
	if (nInstances > 0)
	{
		int i = 0;
		for (i = 0; i < nInstances; i++)
		{

			SPAIopInputPSReference uniqueReference = iInputPSStructure.GetUniqueReference(i);
			SPAIopWString referenceStorageName = uniqueReference.GetStorageName();
			bool isMissingReference = missingFiles.ContainsPath(referenceStorageName);
			if (true == isMissingReference)
			{
				if (uniqueReference.IsPart())
				{
					missingParts++;
				}

			}
		}

	}
	return (totalParts - missingParts);

}

//-----------------------------------------------------------------------------------------

int ImportParts( ImportedPartReferenceList iImportedPartList, const SPAIopPolicy& iPolicy )
{
	int result = SPAIopSampleResult_Import_Ok;
	for( ImportedPartReferenceList::iterator bit = iImportedPartList.begin(); bit != iImportedPartList.end(); ++bit ) 
	{
		SPAIopInputPSReference* pReference = *bit;
		if( nullptr != pReference )
		{
			        PrintPSReferenceInfo(*pReference);
				SPAIopPartDocument partReferneceDoc = pReference->GetDocument();
				int res = ImportPartVisualization( partReferneceDoc, iPolicy );
				if (0 != res)
					result = res;

				if (SPAIopSampleResult_Import_Part_Fail == res)
				{
					unsigned int uniqueID = pReference->GetUniqueID();
					SPAIopWString storageNamePath = pReference->GetStorageName();
					SPAIopWString referenceName = pReference->GetReferenceName();

					SPAIopWString storageName;
					GetNameWithExtension(storageNamePath, storageName);

					const wchar_t* pStorageName = nullptr;
					storageName.ToWChar(pStorageName);

					const wchar_t* pReferenceName = nullptr;
					referenceName.ToWChar(pReferenceName);

					PrintMessage(L"\n FAILURE : Part failed to import. UniqueID = %d, Storage Name = %ls, Reference Name = %ls \n", uniqueID, pStorageName, pReferenceName);

				}
							
			delete pReference;
			pReference = nullptr;
		}
	}
	return result;
}

//-----------------------------------------------------------------------------------------

int ImportPartVisualization( SPAIopPartDocument& iIopPartDocument, const SPAIopPolicy& iPolicy )
{
	// Import the visualization data of a part using the Import method of a 
	// SPAIopAcisVisualizationImporter object.  Use try/catch exception handling
	// blocks to manage fatal errors.
	int result = SPAIopSampleResult_Import_Ok;
	SPAIopVisualizationImporter partVizImporter;
	try
	{
		SPAIopVisuImportResult vizImportResult = partVizImporter.ImportWithPolicy( iPolicy, iIopPartDocument );
		SPAIopVisuData vizData = vizImportResult.GetVisualizationData();

		SPAIopVisualization::AccessVisualizationData(vizData, _preserveStructure);
	}
	catch ( SPAIopAcisError& error )
	{
		DisplayError( error );
		result = SPAIopSampleResult_Import_Part_Fail;
	}
	catch ( ... )
	{
		result = SPAIopSampleResult_Import_Part_Fail;
	}
	return result;
}

//-----------------------------------------------------------------------------------------

void ImportAssemblyLevelVisualization( SPAIopInputProductStructure& iInputPS, const SPAIopPolicy& iPolicy)
{
	try
	{
		SPAIopVisualization::ImportAssemblyLevelVisualization(iInputPS, iPolicy, _preserveStructure);
	}
	catch ( SPAIopAcisError& error  )
	{
		DisplayError( error );
	}
	catch( ... )
	{
		std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
	}
}

//-----------------------------------------------------------------------------------------

int DefineOutputProductStructureMP(SPAIopInputProductStructure& iInputPS, SPAIopPSImportResult &iInputPSResult, const SPAIopPolicy& iPolicy, SPAIopOutputProductStructure& oOutputPS, int& opCopiedInstances)
{
	int result = SPAIopSampleResult_Import_Ok;
	int copiedInstances = 0;
	SPAIopInputPSMissingFileIter missingFiles = iInputPSResult.GetMissingFileIterator();
	SPAIopMultiProcessBatchDefinition batchDef(iInputPS, iPolicy);
	batchDef.AddType(SPAIopRep::SPAIopRep_Visualization);
	SPAIopMultiProcessSpoolerEx spooler(batchDef);
	bool didStart = spooler.InitiateConversion();

	// Loop until all done
	if (!didStart)
	{
		std::wcerr << "ERROR: The conversion was not initiated in parallel" << std::endl;
		return -1;
	}

	std::unordered_map<int, SPAIopInputPSInstance> rootInstanceIDs;
	int nbInstances = iInputPS.GetRootInstancesCount();
	for (int i = 0; i < nbInstances; i++)
	{
		SPAIopInputPSInstance inputRootInstance = iInputPS.GetRootInstance(i);
		SPAIopInputPSReference inputReference = inputRootInstance.GetReference();
		unsigned int refID = inputReference.GetUniqueID();
		rootInstanceIDs[refID] = inputRootInstance;
	}

	std::unordered_map<int, SPAIopOutputPSReference*> idToOutputPSRef;
	std::unordered_map<int, std::pair<std::list<SPAIopInputPSInstance>, std::list<SPAIopOutputPSInstance*>>> refIDToListOfAllInOutInst;
	CreateOutputProductStructureSkeletonRootInstance(iInputPS, iInputPSResult, oOutputPS, idToOutputPSRef, refIDToListOfAllInOutInst);


	SPAIopMultiProcessBatchDefinition::Token_t refToken;
	while (spooler.WaitForNextAvailableReference(refToken) != SPAIopMultiProcessSpoolerEx::WaitResult_t::NoMoreData)
	{
		unsigned int refID = refToken.GetRefID();
		SPAIopRep tokenRepType = refToken.GetImportedRepresentation();
		if (rootInstanceIDs.find(refID) != rootInstanceIDs.end())
		{
			SPAIopInputPSInstance inputRootInstance = rootInstanceIDs[refID];

			bool skipInstance = ShouldInstanceSkipped(inputRootInstance);
			if (true == skipInstance)
			{
				continue;
			}

			SPAIopInputPSReference inputReference = inputRootInstance.GetReference();
			SPAIopWString referenceStorageName = inputReference.GetStorageName();
			bool isMissingReference = missingFiles.ContainsPath(referenceStorageName);
			if (false == isMissingReference)
			{
				unsigned int refID = inputReference.GetUniqueID();
				SPAIopOutputPSReference outputReference = *(idToOutputPSRef[refID]);

				int numDefined = 0;
				bool isShiftTranslationVectorApplied = false;
				double shiftTranslationVector[3] = { 0.0 };
				int res = DefinePSReferenceMP(inputReference, missingFiles, oOutputPS, iPolicy, outputReference, numDefined, idToOutputPSRef, refIDToListOfAllInOutInst, tokenRepType, isShiftTranslationVectorApplied, shiftTranslationVector);
				if (0 != res)
					result = res;
				if (numDefined > 0)
				{
					copiedInstances += numDefined;
					if (isShiftTranslationVectorApplied)
					{
						const auto unorderedMapIterator = refIDToListOfAllInOutInst.find(refID);
						if (unorderedMapIterator != refIDToListOfAllInOutInst.end())
						{
							//Applying shift translation to all the instances having current reference/RefID
							//we don't process previously defined refs again
							while (!(unorderedMapIterator->second.first.empty()))
							{
								SPAIopInputPSInstance inputInstance = unorderedMapIterator->second.first.front();
								SPAIopOutputPSInstance outputPSInstance = *(unorderedMapIterator->second.second.front());
								ModifyInstanceTransform(inputInstance, shiftTranslationVector, outputPSInstance);
								unorderedMapIterator->second.first.pop_front();
								delete (unorderedMapIterator->second.second.front());
								unorderedMapIterator->second.second.pop_front();
							}
						}
					}
				}
			}
		}
		else
		{
			SPAIopPolicy policy = refToken.GetPolicy();
			SPAIopRep availableType = refToken.GetImportedRepresentation();

			SPAIopInputPSReference reference = iInputPS.GetReferenceFromId(refID);
			SPAIopWString format = reference.GetDefinitionDocumentType();

			SPAIopWString referenceStorageName = reference.GetStorageName();
			bool isMissingReference = missingFiles.ContainsPath(referenceStorageName);
			if (false == isMissingReference)
			{
				unsigned int refID = reference.GetUniqueID();
				SPAIopOutputPSReference outputReference = *(idToOutputPSRef[refID]);

				int numDefined = 0;
				bool isShiftTranslationVectorApplied = false;
				double shiftTranslationVector[3] = { 0.0 };
				int res = DefinePSReferenceMP(reference, missingFiles, oOutputPS, iPolicy, outputReference, numDefined, idToOutputPSRef, refIDToListOfAllInOutInst, tokenRepType, isShiftTranslationVectorApplied, shiftTranslationVector);
				if (0 != res)
					result = res;
				if (numDefined > 0)
				{
					copiedInstances += numDefined;
					if (isShiftTranslationVectorApplied)
					{
						const auto unorderedMapIterator = refIDToListOfAllInOutInst.find(refID);
						if (unorderedMapIterator != refIDToListOfAllInOutInst.end())
						{
							//Applying shift translation to all the instances having current reference/RefID
							//we don't process previously defined refs again
							while (!(unorderedMapIterator->second.first.empty()))
							{
								SPAIopInputPSInstance inputInstance = unorderedMapIterator->second.first.front();
								SPAIopOutputPSInstance outputPSInstance = *(unorderedMapIterator->second.second.front());
								ModifyInstanceTransform(inputInstance, shiftTranslationVector, outputPSInstance);
								unorderedMapIterator->second.first.pop_front();
								delete (unorderedMapIterator->second.second.front());
								unorderedMapIterator->second.second.pop_front();
							}
						}
					}
				}
			}
		}
	}
	opCopiedInstances = copiedInstances;

	//Clearing maps to avoid memory leak
	for (auto unorderedMapIterator = refIDToListOfAllInOutInst.begin(); unorderedMapIterator != refIDToListOfAllInOutInst.end();)
	{
		for (auto listIterator = unorderedMapIterator->second.first.begin(); listIterator != unorderedMapIterator->second.first.end();)
		{
			listIterator = unorderedMapIterator->second.first.erase(listIterator);
		}

		for (auto listIterator = unorderedMapIterator->second.second.begin(); listIterator != unorderedMapIterator->second.second.end();)
		{
			delete* listIterator;
			listIterator = unorderedMapIterator->second.second.erase(listIterator);
		}

		if (unorderedMapIterator->second.first.empty() && unorderedMapIterator->second.second.empty())
		{
			unorderedMapIterator = refIDToListOfAllInOutInst.erase(unorderedMapIterator);
		}
		else
			++unorderedMapIterator;
	}
	refIDToListOfAllInOutInst.clear();

	for (auto unorderedMapIterator = idToOutputPSRef.begin(); unorderedMapIterator != idToOutputPSRef.end(); ++unorderedMapIterator)
	{
		delete unorderedMapIterator->second;
	}
	idToOutputPSRef.clear();

	return result;
}



void CreateOutputProductStructureSkeletonRootInstance(SPAIopInputProductStructure& iInputPS, SPAIopPSImportResult &iInputPSResult, SPAIopOutputProductStructure& oOutputPS, std::unordered_map<int, SPAIopOutputPSReference*>& iIdToOutputPSRef, std::unordered_map<int, std::pair<std::list<SPAIopInputPSInstance>, std::list<SPAIopOutputPSInstance*>>>& iRefIDToListOfAllInOutInst)
{
	SPAIopInputPSMissingFileIter missingFiles = iInputPSResult.GetMissingFileIterator();
	int nbInstances = iInputPS.GetRootInstancesCount();
	int i = 0;
	for (i = 0; i < nbInstances; i++)
	{
		// Get Input PS info (instance, reference, ID)
		SPAIopInputPSInstance inputRootInstance = iInputPS.GetRootInstance(i);

		bool skipInstance = ShouldInstanceSkipped(inputRootInstance);
		if (true == skipInstance)
		{
			continue;
		}

		SPAIopInputPSReference inputReference = inputRootInstance.GetReference();
		SPAIopWString referenceStorageName = inputReference.GetStorageName();
		bool isMissingReference = missingFiles.ContainsPath(referenceStorageName);
		if (false == isMissingReference)
		{
			unsigned int refID = inputReference.GetUniqueID();

			// Replicate the same on outPS
			SPAIopOutputPSReference* outputReference = NULL;

			//If RefID entry already present in map get that reference
			if (iIdToOutputPSRef.find(refID) != iIdToOutputPSRef.end())
				outputReference = iIdToOutputPSRef[refID];
			else
				outputReference = new SPAIopOutputPSReference(oOutputPS, refID); // refID is unique and used to identify the reference

			iIdToOutputPSRef.emplace(refID, outputReference);
			SPAIopOutputPSInstance* outputInstance = new SPAIopOutputPSInstance(*outputReference);
			DefinePSInstance(inputRootInstance, *outputInstance);
			iRefIDToListOfAllInOutInst[refID].first.push_back(inputRootInstance);
			iRefIDToListOfAllInOutInst[refID].second.push_back(outputInstance);

			oOutputPS.AddToRoot(*outputInstance);
			CreateOutputProductStructureSkeleton(inputReference, missingFiles, oOutputPS, *outputReference, iIdToOutputPSRef, iRefIDToListOfAllInOutInst);
		}
	}
}


void CreateOutputProductStructureSkeleton(SPAIopInputPSReference& iInputReference, SPAIopInputPSMissingFileIter& iMissingFiles, SPAIopOutputProductStructure& iOutputPS, SPAIopOutputPSReference& oOutputReference, std::unordered_map<int, SPAIopOutputPSReference*>& iIdToOutputPSRef, std::unordered_map<int, std::pair<std::list<SPAIopInputPSInstance>, std::list<SPAIopOutputPSInstance*>>>& iRefIDToListOfAllInOutInst)
{
	if (!oOutputReference.IsDefined())
	{
		if (!iInputReference.IsPart())
		{
			SPAIopWString referencePID;
			bool hasPID = iInputReference.GetPID(referencePID);
			if (hasPID)
			{
				const wchar_t* refName = nullptr;
				referencePID.ToWChar(refName);
				SPAIopOutputPSReference* pReference = &oOutputReference;
				TrackReferences(add_subassembly, refName, pReference);
			}

			int nbInstances = iInputReference.GetInstancesCount();
			if (nbInstances > 0)
			{
				int i = 0;
				for (i = 0; i < nbInstances; i++)
				{
					// Get Input Product Structure info (instance, reference, ID)
					SPAIopInputPSInstance iNodeInstance = iInputReference.GetInstance(i);

					bool skipInstance = ShouldInstanceSkipped(iNodeInstance);
					if (true == skipInstance)
					{
						continue;
					}

					SPAIopInputPSReference iNodeReference = iNodeInstance.GetReference();
					SPAIopWString referenceStorageName = iNodeReference.GetStorageName();
					bool isMissingReference = iMissingFiles.ContainsPath(referenceStorageName);
					if (false == isMissingReference)
					{
						unsigned int refID = iNodeReference.GetUniqueID();

						// Replicate the same on outPS
						SPAIopOutputPSReference* newOutputReference = NULL;

						//If RefID entry already present in map get that reference
						if (iIdToOutputPSRef.find(refID) != iIdToOutputPSRef.end())
							newOutputReference = iIdToOutputPSRef[refID];
						else
							newOutputReference = new SPAIopOutputPSReference(iOutputPS, refID); // refID is unique and used to identify the reference

						iIdToOutputPSRef.emplace(refID, newOutputReference);
						SPAIopOutputPSInstance* iOutputInstance = new SPAIopOutputPSInstance(*newOutputReference);
						DefinePSInstance(iNodeInstance, *iOutputInstance);
						iRefIDToListOfAllInOutInst[refID].first.push_back(iNodeInstance);
						iRefIDToListOfAllInOutInst[refID].second.push_back(iOutputInstance);

						oOutputReference.AddInstance(*iOutputInstance);
						CreateOutputProductStructureSkeleton(iNodeReference, iMissingFiles, iOutputPS, *newOutputReference, iIdToOutputPSRef, iRefIDToListOfAllInOutInst);
					}
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------------------


int DefineOutputProductStructure(SPAIopInputProductStructure& iInputPS,SPAIopPSImportResult &iInputPSResult, const SPAIopPolicy& iPolicy, SPAIopOutputProductStructure& oOutputPS,int& opCopiedInstances)
{
	// Navigate all of the nodes of the input product structure and create the corresponding output product
	// structure (SPAIopOutputProductStructure) and its out instances (SPAIopOutputPSInstance) and output references (SPAIopOutputPSReference)
	int result = 0;
	bool success = true;

	SPAIopInputPSMissingFileIter missingFiles = iInputPSResult.GetMissingFileIterator();

	SPAIopWString productName = iInputPS.GetProductName();
	oOutputPS.SetProductName( productName );

	// build a map of references to use later for attaching assembly annotations
	const wchar_t* prodName = nullptr;
	SPAIopWString productPID;
	bool hasPID = iInputPS.GetPID(productPID);
	if(hasPID)
		productPID.ToWChar( prodName );
	SPAIopOutputPSReference* noRef = nullptr;
	TrackReferences( set_root, prodName, noRef );

	if (_runInMP)
	{
		return DefineOutputProductStructureMP(iInputPS, iInputPSResult, iPolicy, oOutputPS, opCopiedInstances);
	}

	SPAIopWorldOrientation orientation = SPAIopWorldOrientation_Unknown;
	iInputPS.GetWorldOrientation(orientation);
	oOutputPS.SetWorldOrientation(orientation);

	//Multiple instances having same reference will need below map to store shift translation vector for each unique reference
	//This is done as we don't process already defined/processed reference again
	std::unordered_map<int, std::pair<bool, double[3]>> refIDToShiftTranslationMap;
	int nbInstances = iInputPS.GetRootInstancesCount(); // this should be 1!
	int copiedInstances = 0;
	for( int i = 0; i < nbInstances && true == success; i++ )
	{
		// Get Input PS info (instance, reference, ID)
		SPAIopInputPSInstance inputRootInstance = iInputPS.GetRootInstance(i);
		SPAIopInputPSReference inputReference = inputRootInstance.GetReference();
		SPAIopWString referenceStorageName = inputReference.GetStorageName();
		bool isMissingReference = missingFiles.ContainsPath( referenceStorageName );
		if ( false == isMissingReference )
		{
			unsigned int refID = inputReference.GetUniqueID();

			// Replicate the same on outPS
			SPAIopOutputPSReference outputReference( oOutputPS, refID ); // refID is unique and used to identify the reference

			int numDefined = 0;
			bool isShiftTranslationVectorApplied = false;
			double shiftTranslationVector[3] = { 0.0 };
			int res = DefinePSReference(inputReference, missingFiles, oOutputPS, iPolicy, outputReference,numDefined, refIDToShiftTranslationMap, isShiftTranslationVectorApplied, shiftTranslationVector);
			if (0 != res)
				result = res;
			if( numDefined > 0 )
			{
				copiedInstances += numDefined;

				SPAIopOutputPSInstance outputInstance( outputReference );
				DefinePSInstance( inputRootInstance, outputInstance );
				if (isShiftTranslationVectorApplied)
				{
					ModifyInstanceTransform(inputRootInstance, shiftTranslationVector, outputInstance);
					refIDToShiftTranslationMap[refID].first = isShiftTranslationVectorApplied;
					std::copy(std::begin(shiftTranslationVector), std::end(shiftTranslationVector), std::begin(refIDToShiftTranslationMap[refID].second));
				}
				else
				{
					//Case where reference is already defined but have shift translation vector
					const auto unorderedMapIterator = refIDToShiftTranslationMap.find(refID);
					if (unorderedMapIterator != refIDToShiftTranslationMap.end())
					{
						isShiftTranslationVectorApplied = unorderedMapIterator->second.first;
						if (isShiftTranslationVectorApplied)
						{
							shiftTranslationVector[0] = unorderedMapIterator->second.second[0];
							shiftTranslationVector[1] = unorderedMapIterator->second.second[1];
							shiftTranslationVector[2] = unorderedMapIterator->second.second[2];
							ModifyInstanceTransform(inputRootInstance, shiftTranslationVector, outputInstance);
						}
					}
				}

				oOutputPS.AddToRoot( outputInstance );
			}
		}
	}
	opCopiedInstances = copiedInstances;

	refIDToShiftTranslationMap.clear();

	return result;
}

//-----------------------------------------------------------------------------------------

int DefinePSReference( SPAIopInputPSReference& iInputReference, SPAIopInputPSMissingFileIter& iMissingFiles, SPAIopOutputProductStructure& iOutputPS, const SPAIopPolicy& iPolicy, SPAIopOutputPSReference& oOutputReference,int& opNumDefined, std::unordered_map<int, std::pair<bool, double[3]>>& iRefIDToShiftTranslationMap, bool& oIsShiftTranslationVectorApplied, double(&oShiftTranslation)[3])
{
	int numDefined = 0;
	int result = 0;
	if( !oOutputReference.IsDefined())
	{
		if( iInputReference.IsPart())    // part node
		{
			// Import the visualization data of a part using the Import method of a 
			// SPAIopAcisVisualizationImporter object.  Use try/catch exception handling
			// blocks to manage fatal errors.

			SPAIopVisualizationImporter partVizImporter;
			SPAIopPartDocument refPartDocument = iInputReference.GetDocument();
			unsigned int uniqueID = iInputReference.GetUniqueID();
			SPAIopWString storageNamePath = iInputReference.GetStorageName();
			SPAIopWString referenceName = iInputReference.GetReferenceName();

			SPAIopWString storageName;
			GetNameWithExtension(storageNamePath, storageName);

			const wchar_t* pStorageName = nullptr;
			storageName.ToWChar(pStorageName);

			const wchar_t* pReferenceName = nullptr;
			referenceName.ToWChar(pReferenceName);
			try
			{
				SPAIopVisuImportResult vizImportResult = partVizImporter.ImportWithPolicy( iPolicy, refPartDocument );
				PrintMessage(L"\n Processing reference part. UniqueID = %d, Storage Name = %ls, Reference Name = %ls \n", uniqueID, pStorageName, pReferenceName);
				SPAIopVisuData vizInData = vizImportResult.GetVisualizationData();

				oIsShiftTranslationVectorApplied = vizImportResult.GetShiftTranslation(oShiftTranslation[0], oShiftTranslation[1], oShiftTranslation[2]);
				if (oIsShiftTranslationVectorApplied)
				{
					PrintMessage(L"\nGeometry is shifted near the origin to fit it inside the current modeler range, Following Shift Translation Vector should be applied on the part to get the original input coordinates of geometry:- X: %lf\tY: %lf\tZ: %lf\n", oShiftTranslation[0], oShiftTranslation[1], oShiftTranslation[2]);
				}

				const SPAIopLengthUnitType visualizationUnit = vizInData.GetUnits();
				if (visualizationUnit != SPAIopLengthUnit_Unknown)
				{
					SPAIopWString unitStr = SPAIopLengthUnitUtils::LengthUnitToString(visualizationUnit);
					const wchar_t* pUnitArray = NULL;
					unitStr.ToWChar(pUnitArray);
					PrintMessage(L"\nUnit of Visualization Data:- %ls\n", pUnitArray);

					//Scaling Shift translation vector to modeler unit as we will accomodate it as instance transform, instance transform is given in modeler unit
					if (oIsShiftTranslationVectorApplied)
					{
						double unitScale = 1.0;
						SPAIopLengthUnitType modelerUnit = SPAIopLengthUnit_Unknown;
						SPAIopModelerOptionsAcis acisModelerOptions;
						const bool res = iPolicy.GetCurrentModelerOptions(acisModelerOptions);
						if (res)
						{
							const bool foundOption = acisModelerOptions.GetCurrentModelerUnits(modelerUnit);
							if (!foundOption)
								modelerUnit = SPAIopLengthUnit_Millimeter;
						}
						else
							modelerUnit = SPAIopLengthUnit_Millimeter;

						unitScale = SPAIopLengthUnitUtils::GetScalingFactor(visualizationUnit, modelerUnit);
						oShiftTranslation[0] = oShiftTranslation[0] * unitScale;
						oShiftTranslation[1] = oShiftTranslation[1] * unitScale;
						oShiftTranslation[2] = oShiftTranslation[2] * unitScale;
					}
				}

				if (!_useInputVisualization)
				{
					SPAIopVisuOutputData vizOutData;
					numDefined += TransferVisualizationData(vizInData, vizOutData);
					oOutputReference.SetOutputVisualization(vizOutData);
				}
				else
				{
					oOutputReference.SetVisualization(vizInData);
					if (0 == numDefined)
					{
						numDefined += 1;
					}
				}
			}
			catch ( SPAIopAcisError& error )
			{
				result = SPAIopSampleResult_Import_Part_Fail;
				PrintMessage(L"\n FAILURE : Part failed to import. UniqueID = %d, Storage Name = %ls, Reference Name = %ls \n", uniqueID, pStorageName, pReferenceName);
				DisplayError( error );				
			}
			catch ( ... )
			{
				PrintMessage(L"\n FAILURE : Part failed to import. UniqueID = %d, Storage Name = %ls, Reference Name = %ls \n", uniqueID, pStorageName, pReferenceName);
				result = SPAIopSampleResult_Import_Part_Fail;
			}
		}
		else   // sub-assembly node
		{
			// build a map of references to use later for attaching assembly annotations
			const wchar_t* refName = nullptr;
			SPAIopWString referencePID;
			bool hasPID = iInputReference.GetPID(referencePID);
			if(hasPID)
				referencePID.ToWChar( refName );
			SPAIopOutputPSReference* pReference = &oOutputReference;
			TrackReferences( add_subassembly, refName, pReference );

			int nbInstances = iInputReference.GetInstancesCount();
			if ( nbInstances > 0 )
			{
				for ( int i = 0; i < nbInstances; i++ )
				{
					// Get Input Product Structure info (instance, reference, ID)
					SPAIopInputPSInstance iNodeInstance = iInputReference.GetInstance(i);
					SPAIopInputPSReference iNodeReference = iNodeInstance.GetReference();
					SPAIopWString referenceStorageName = iNodeReference.GetStorageName();
					bool isMissingReference = iMissingFiles.ContainsPath( referenceStorageName );
					if ( false == isMissingReference )
					{
						unsigned int refID = iNodeReference.GetUniqueID();

						// Replicate the same for the output product structure
						SPAIopOutputPSReference newOutputReference(iOutputPS, refID);
						int entities = 0;
						bool isShiftTranslationVectorApplied = false;
						double shiftTranslationVector[3] = { 0.0 };
						int res = DefinePSReference(iNodeReference, iMissingFiles, iOutputPS, iPolicy, newOutputReference, entities, iRefIDToShiftTranslationMap, isShiftTranslationVectorApplied, shiftTranslationVector);// Recursively navigate nodes below this sub-assembly node
						if (0 != res)
							result = res;
						if( entities > 0 )
						{
							SPAIopOutputPSInstance iOutputInstance( newOutputReference );
							DefinePSInstance( iNodeInstance, iOutputInstance );
							if (isShiftTranslationVectorApplied)
							{
								ModifyInstanceTransform(iNodeInstance, shiftTranslationVector, iOutputInstance);
								iRefIDToShiftTranslationMap[refID].first = isShiftTranslationVectorApplied;
								std::copy(std::begin(shiftTranslationVector), std::end(shiftTranslationVector), std::begin(iRefIDToShiftTranslationMap[refID].second));
							}
							else
							{
								//Case where reference is already defined but have shift translation vector
								const auto unorderedMapIterator = iRefIDToShiftTranslationMap.find(refID);
								if (unorderedMapIterator != iRefIDToShiftTranslationMap.end())
								{
									isShiftTranslationVectorApplied = unorderedMapIterator->second.first;
									if (isShiftTranslationVectorApplied)
									{
										shiftTranslationVector[0] = unorderedMapIterator->second.second[0];
										shiftTranslationVector[1] = unorderedMapIterator->second.second[1];
										shiftTranslationVector[2] = unorderedMapIterator->second.second[2];
										ModifyInstanceTransform(iNodeInstance, shiftTranslationVector, iOutputInstance);
									}
								}
							}

							oOutputReference.AddInstance( iOutputInstance );
							numDefined += entities;
						}
					}
				}
			}
		}

        // In some formats supporting delayed part loading, we do not get reference name without loading (importing) part first. So set it after part is imported.
        SPAIopWString referenceName = iInputReference.GetReferenceName();
        oOutputReference.SetName( referenceName );

        // mark this reference as fully defined so we don't go through this code again if it is used by another instance
		if ( numDefined > 0 ) 
		{
			oOutputReference.SetDefined( true );
		}
	} 
	else 
	{
		// Don't do anything for previously defined refs, but pretend they were defined this time
		numDefined = 1;
	}

	opNumDefined = numDefined;
	return result;
}

//-----------------------------------------------------------------------------------------

int test_IopAcisVisualization(int argc, char* argv[])
{
	int result( 1 );
	SPAIopPerformanceStatistics printPerfoStatsObj;

	try
	{
		char* argFields[] = { "i", "o", "dump", "p", "s", "v", "useInputVisu", "h", "mode", "visuCurveData", "log" };
		int size = sizeof(argFields) / sizeof(*argFields);
		SPAIopArgParser parser(size, argFields, argc, argv, &PrintHelp);
		// IOP is called in its own scope.
		// This sample uses a guard class that initializes InterOp upon 
		// construction and terminates InterOp upon destruction.  All 
		// translations are done within the boundaries of a single 
		// initialization and shutdown.  InterOp may only be initialized 
		// and shutdown once per process.  Please do not use the guard 
		// classes multiple times per process.
		SPAIopAcisSystemInitGuard initGuard(SPATIAL_LICENSE);

		result = initGuard.GetInitStatus();

		if (true == parser.IsSuccess())
		{			
			int structure = 1;
			if (parser.GetIntegerValueFor("s", structure))
				_preserveStructure = (structure != 0);

			int verbosity = (int)VERBOSITY_LEVEL_PRIMITIVE;
			parser.GetIntegerValueFor("v", verbosity);
			if (verbosity >= VERBOSITY_LEVEL_NONE && verbosity <= VERBOSITY_LEVEL_PRIMITIVE)
			{
				SetVerbosity((verbosity_level)verbosity);
			}

			const wchar_t* pSourceFilePath = nullptr;
			bool reading = parser.GetWStringValueFor("i", pSourceFilePath);

			const wchar_t* pDestinationFilePath = nullptr;
			bool writing = parser.GetWStringValueFor("o", pDestinationFilePath);

			const wchar_t* pInputMode = L"";
			bool modeFlag = parser.GetWStringValueFor("mode", pInputMode);
			if (wcsncmp(pInputMode, L"MP", 2) == 0)
				_runInMP = true;

			const wchar_t* pConsoleOutputFilePath = nullptr;
			bool hasConsoleOutputFilePath = (parser.GetWStringValueFor("dump", pConsoleOutputFilePath));

			if (hasConsoleOutputFilePath)
			{
				if (false == OpenFileForConsoleOutput(pConsoleOutputFilePath))
					return 11;
			}

			if (SPA_UNLOCK_NO_MSG == result)
			{
				// Spatial products have been unlocked
				std::wcout << L"SUCCESS: SPAIopAcisSystem::Initialize. " << std::endl;
			}
			else if (SPA_UNLOCK_KEY_WARN_EXPIRE == result)
				std::wcout << L"SPAIopAcisSystem::Initialize returned SPA_UNLOCK_KEY_WARN_EXPIRE; your license key will expire soon!\n";
			else if (SPA_UNLOCK_REGISTRATION_WARN_EXPIRE == result)
				std::wcout << L"SPAIopAcisSystem::Initialize returned SPA_UNLOCK_REGISTRATION_WARN_EXPIRE; your registration will expire soon.\n";
			else
			{
				std::wcout << L"SPAIopAcisSystem::Initialize returned an unexpected value; Contact Spatial Customer Support or your Account Manager.\n";
				return 1;
			}
			result = 1;

			const wchar_t* pPolicyFilePath = nullptr;
			bool hasPolicyFile = parser.GetWStringValueFor("p", pPolicyFilePath);

			const wchar_t* pLogFilePath = NULL;
			bool hasLogFileName = parser.GetWStringValueFor("log", pLogFilePath);

		SPAIopPolicy policy;
		if ( 0 != pPolicyFilePath )
		{
			SPAIopWString policyFilePath( pPolicyFilePath );
			SPAIopPolicyFileStreamXML xmlPolicyStream;
			xmlPolicyStream.SetUnstreamFilePath( policyFilePath );

				policy.Deserialize(xmlPolicyStream);
			}

		SetOrUpdateLogFileInPolicy(pLogFilePath, policy);

			int inputVisuFlag = 0;
			bool hasInputVisuFlag = parser.GetIntegerValueFor("useInputVisu", inputVisuFlag);

			if (hasInputVisuFlag && (inputVisuFlag != 0))
			{
				_useInputVisualization = true;
			}

			int visuCurveDataFlag = 0;
			bool hasvisuCurveDataFlag = parser.GetIntegerValueFor("visuCurveData", visuCurveDataFlag);

			if (hasvisuCurveDataFlag && (visuCurveDataFlag != 0))
			{
				SPAIopVisualization::SetIsVisuCurveData(true);
			}

			if (writing)
			{
				if (reading)
				{
					result = ImportExportVisualizationData(pSourceFilePath, pDestinationFilePath, policy);
				}
				else /* writing but not reading */
				{
					if (ExportSyntheticData(pDestinationFilePath, policy))
					{
						result = 0;
					}
				}
			}
			else if (reading) /* reading but not writing */
			{
				result = ImportVisualizationData(pSourceFilePath, policy);
			}

			if (true == hasConsoleOutputFilePath)
				CloseFileForConsoleOutput();
		}
	
	}
	catch (SPAIopAcisError& fatalErr)
	{
		DisplayError(fatalErr);
		result = 1;
	}
	catch (...)
	{
		PrintMessage(L"FAILURE: Unknown exception caught! ");
		result = 1;
	}

    return result;
} 

//-----------------------------------------------------------------------------------------

